import AStar,time,copy
import csv
import pygame
import random

#@DESC:
#@TODO:
class Tile:
  def __init__(self):
    self.type = None
    self.squads = []
    self.terrain = []
    
#@DESC:
#@TODO:    
class Map:
  def __init__(self,code_map):
    self.map = []
    self.tiles_with_squads = []
    self.tiles = []
    self.vantage_points = []
    
    for i in range(1024):
      self.tiles.append(Tile())
    self.code_map = code_map
    self.pathfinding = AStar.AStar(AStar.SQ_MapHandler(self,32,32))
  
  def getTile(self,x,y):
    return self.tiles[x*32+y]
    
  def rotateSquads(self,x,y):
    if len(self.getTile(x,y).squads)>1:
      first = self.getTile(x,y).squads[0]
      self.getTile(x,y).squads = self.getTile(x,y).squads[1:]
      self.getTile(x,y).squads.append(first)
  
  def addVantagePoints(self,x,y,vantage_data):
    #self.getTile(x,y).vantage_data =
      self.vantage_points.append([x,y])
      
  def clearVantagePoints(self):
    pass
    
  def addSquadLink(self,x,y,squad):
    self.getTile(x,y).squads.append(squad)
    if len(self.getTile(x,y).squads)==1:
      self.tiles_with_squads.append((x,y))
    
  def removeSquadLink(self,x,y,squad):
    self.getTile(x,y).squads.remove(squad)
    if len(self.getTile(x,y).squads)==0:
      self.tiles_with_squads.remove((x,y))
    
  def findPath(self,pos,dst,type = 'ground'):
    ret = []
    tmp = self.pathfinding.findPath(AStar.SQ_Location(pos[0],pos[1]),AStar.SQ_Location(dst[0],dst[1]),type)
    if not tmp is None:
      for el in tmp.getNodes():
        ret.append((el.location.x,el.location.y))
    else:
      return []
    return ret 
    
  def get(self,x,y,type):
    if len(self.getTile(x,y).squads)>3:
      for sq in self.getTile(x,y).squads:
        if sq.order_status == 'redeploy':
          self.code_map.get((x,y))
        else:
          return 100
    else:
      return self.code_map.get((x,y))    
  def save(self):
    pass
  #~ def load(self):
    #~ pass



    


#@DESC:
#@TODO:
def readTable(name):
  values = csv.reader(open(name))
  ret = {}
  header = values.next()[1:]
  for type in values:
    ret[type[0]] = {}
    for index in range(len(header)):
      ret[type[0]][header[index]] = type[index+1]
  return ret



#@DESC:
#@TODO:
class Commander:
  def __init__(self,name,input_mode):
    self.name = name
    self.input_mode = input_mode
    self.order_stack = []
    self.squad = []
    self.visible_enemy_units = []



def sumFirepower(a,b):
  return [a[0]+(b[0]),a[1]+(1.0 - a[1])*b[1]]
  
#@DESC:
#@TODO:
class Game:
  def __init__(self,name,code_map):
    random.seed(100)
    self.commanders = []
    self.playable_commander = None
    self.database = Database()
    self.map = Map(code_map)
    self.time_zero = 0
    self.time = 0
    self.time_step = 900
    self.preview_step = 60
    self.sides = []
    self.squads = []
    self.layers = []
    self.controls = []
    self.vantage_points = []
    self.states = ['zero_phase','squad_movement','strategy_choice']
    self.active_state = []
    self.selected_squad = None
    self.views =['squad_control','squad_info']
    self.active_view = 'squad_control'
    self.pt = {}
        
    self.time_zero = time.strptime("30 Nov 10", "%d %b %y")
    
  #calculation logic funcs
  def unload(self,squad):
    if squad.motorized_state == "loaded":
      sq2 = self.splitSquad(squad,[squad.units[0]],squad.units[1:])
      sq2.completeLastOrder()
      sq2.icon = "infantry"
      squad.motorized_state = "unloaded"
      squad.completeLastOrder()
      
  def load(self,squad):
    if squad.motorized_state == "unloaded":
      self.mergeSquads(squad,squad.bound_squad)
      squad.motorized_state = "loaded"
      squad.completeLastOrder()
    
  def calculateMorale(self,squad):
    pass
  def calculateSpeed(self,squad,tile_info):
    speed = squad.speed * se(squad.chassis,map.getTile(x,y).terrain) * squad.fatigue
    return speed

  def calculaterReactions(self):
    pass
    

      
  #squad orders functions
  def is_visible(self,unit):
    pass
  def calculateFieldOfView(self):
    pass
  def attack(self,squad):
    if not self.enemyAtTile(squad.orders[0].target[0][0],squad.orders[0].target[0][1]):
       squad.completeLastOrder()
    else:
      self.calculateAttack(squad)
      #self.destroySquad(self.map.getTile(squad.orders[0].target[0][0],squad.orders[0].target[0][1]).squads[0])
      
  def calculateAttack(self,squad):
    firepower = [0.0,0.0]
    AT_fire = [0.0,0.0]
    AA_fire = [0.0,0.0]
    sniping_fire = [0.0,0.0]
    for unit in squad.units:
      if squad.motorized_state == "loaded" and not self.database.getUnitInfo(unit.name)['type'] == 'transport':
        continue
      
      info = self.database.getWeaponInfo(self.database.getUnitInfo(unit.name)['main_w'])
      
      if info is None:
        pass
      elif info['damage_type'] == 'automatic_fire':
        firepower = sumFirepower(firepower,[float(info['firepower']),float(info['precision'])])
      elif info['damage_type'] == 'shaped_charge' :
        firepower = sumFirepower(firepower,[float(info['firepower']),float(info['precision'])])
        AT_fire = [AT_fire[0]+float(info['firepower']),AT_fire[1]+(1.0 - AT_fire[1])*float(info['precision'])]
      
    
    vantagePoint = squad.orders[0].target[0][0],squad.orders[0].target[0][1]
    if vantagePoint not in self.vantage_points:
      self.vantage_points[vantagePoint] = [firepower,AT_fire]
    else:
      self.vantage_points[vantagePoint][0] = sumFirepower(self.vantage_points[vantagePoint][0],firepower)
      self.vantage_points[vantagePoint][1] = sumFirepower(self.vantage_points[vantagePoint][1],AT_fire)
     
  
  def calculateDefense(self,squad):
    squad.infantry_defense = 1.0
    if squad.motorized_state == "loaded":
      squad.infantry_defense += 2.0
    squad.armor_defense = 1.0
    
    
  def seekAndDestroy(self,squad):
    pass
  
  def destroySquad(self,squad):
    if squad in self.squads:
      self.map.removeSquadLink(squad.pos[0],squad.pos[1],squad)
      if squad == self.selected_squad:
        self.selected_squad = None
      self.squads.remove(squad)
    
  def autoReturnFire(self,squad):
    pass
    
  def redeploy(self,squad,complete = True):
    
    if squad.order_status == 'starting':
      squad.order_status = 'proceeding'
      self.setPath(squad,squad.orders[0].target[0])
      
    if squad.subposition >= 100:
      self.map.removeSquadLink(squad.pos[0],squad.pos[1],squad)
      squad.pos = squad.path[0]
      self.map.addSquadLink(squad.pos[0],squad.pos[1],squad)
      squad.subposition = 0
      if(len(squad.path)>1):
        squad.path = squad.path[1:]
      else:
        squad.path = []
        if complete:
          squad.completeLastOrder()
    else:
      if len(squad.path) > 0:
        if len(self.map.getTile(squad.path[0][0],squad.path[0][1]).squads)>3:
          if len(squad.path)<2:
            if complete:
              squad.completeLastOrder()
          else:
            self.setPath(squad,squad.path[-1])
        else:
          squad.subposition = squad.subposition + int(self.database.units['btr80']['max_speed'])

  def mergeSquads(self,squad1,squad2):
    for unit in squad2.units:
      squad1.units.append(unit)
    
    self.map.removeSquadLink(squad2.pos[0],squad2.pos[1],squad2)
    self.squads.remove(squad2)
    
    
  def splitSquad(self,squad,units1,units2):
      #self.addNamedSquad(self.playable_commander.name,'mechanized',squad.pos)
      self.squads.append(Squad(squad.name,squad.name,"infantry"))
      for unit in units2:
        squad.units.remove(unit)
      
      self.squads[-1].icon = 'infantry'
      self.squads[-1].units = []
      self.squads[-1].pos = squad.pos
      self.squads[-1].commander = self.playable_commander.name
      
      self.map.addSquadLink(self.squads[-1].pos[0],self.squads[-1].pos[1],self.squads[-1])
      
      for unit in units2:
        self.squads[-1].units.append(unit)
      squad.bound_squad = self.squads[-1]
      self.squads[-1].bound_squad = squad
      return self.squads[-1]
      
  def addNamedSquad(self,commander,name,pos):
      self.squads.append(self.database.getSquad(name))
      self.squads[-1].pos = pos
      self.squads[-1].commander = commander
      self.map.addSquadLink(self.squads[-1].pos[0],self.squads[-1].pos[1],self.squads[-1])
      

  def recon(self,area,squad):
    self.awarness = 3
  def assault(self,area):
    self.awarness = 1
    self.attack_bonus = 2
    self.support_usage = True
    


  def update(self,squad):
    if squad.order_status == 'proceeding' or squad.order_status == 'starting':
      if squad.morale > 4:
        if squad.orders[0].name == 'redeploy':
          self.redeploy(squad)
        elif squad.orders[0].name == 'attack':
          if (abs(squad.pos[0] - squad.orders[0].target[0][0]))>1 or abs(squad.pos[1] - squad.orders[0].target[0][1])>1:
            self.redeploy(squad)
          else:
            self.attack(squad)
        elif squad.orders[0].name == 'unload':
          self.unload(squad)
        elif squad.orders[0].name == 'load':
          if squad.pos != (squad.orders[0].target[0][0],squad.orders[0].target[0][1]):
            self.redeploy(squad,complete = False)
          else:
            self.load(squad)
          
        elif squad.orders[0].name == 'assault':
          pass
        elif squad.orders[0].name == 'suppress':
          pass
        elif squad.orders[0].name == 'patrol':
          pass
      else:
        if squad.morale == 0:
          pass
          
    self.calculateDefense(squad)
      
  def update_commanders(self,squad):
    pass
  
  #Utility functions
  def setPath(self,squad,dst):
    squad.path = self.map.findPath(squad.pos,dst)
    squad.subposition = 0

  def processOrders(self,squad):
    pass
    
  def addOrder(self,squad,order):
    if self.playable_commander is None or squad.commander != self.playable_commander.name:
      return 
    if squad.comm_link == 'active':
      squad.orders.append(order)
      if squad.order_status == 'awaiting':
        squad.order_status = 'starting'
    else:
      'squad is uncontrollable'
  
  def getVantagePoint(self,x,y):
    pass
    
  def results(self):
    for a in self.vantage_points:
      for squad in self.map.getTile(a[0],a[1]).squads:
        for unit in squad.units:
          if self.database.getUnitInfo(unit.name)['armor'] == "steel":
           ran = float(random.randint(1,20))
           if ran*self.vantage_points[a][1][1]*0.20 >=squad.armor_defense:
             if self.vantage_points[a][1][0] > 0:
              squad.units.remove(unit)
              self.vantage_points[a][0][0] -= 1.0
              if squad.motorized_state == "loaded":
                self.destroySquad(squad)
                break
                
          if self.database.getUnitInfo(unit.name)['armor'] == "flak":
           if float(random.randint(0,20))*self.vantage_points[a][0][1]*0.20 >=squad.infantry_defense:
            if self.vantage_points[a][0][0] > 0:
              squad.units.remove(unit)
              self.vantage_points[a][0][0] -= 1.0
          
            
  #well, we making a step
  def makeStep(self,time):
    self.vantage_points = {}
    #~ for commander in self.commanders:
      #~ for order in commander.order_stack:
        #~ addOrder(order[0],order[1])
      
    for counter in range(1):
      for squad in self.squads:
        self.update(squad)
        
    
    self.results()
    #processing planes
    
    
  #Input functions    
  def injectMousePos(self,x,y):
    pass
  def enemyAtTile(self,x,y):
    squads = self.map.getTile(x,y).squads
    if len(squads)<1:
      return False
    if squads[0].commander!=self.playable_commander.name:
      return True
    return False
    
    
  def injectClickPos(self,x,y):
    if x >= 0 and y >= 0 and y<32 and x <32:
      button = pygame.mouse.get_pressed()
      if button == (1,0,0):
        if self.active_view == 'squad_control':
          if len(self.map.getTile(x,y).squads)>0:
            #if self.playable_commander is not None and self.map.getTile(x,y).squads[0].commander == self.playable_commander.name:
              if self.selected_squad == self.map.getTile(x,y).squads[0]:
                self.map.rotateSquads(x,y)
              self.selected_squad = self.map.getTile(x,y).squads[0]
      if self.selected_squad is not None:
        if button == (0,0,1):
            if self.enemyAtTile(x,y):
              self.addOrder(self.selected_squad,Order('attack',0,[x,y]))
            else:
              self.addOrder(self.selected_squad,Order('redeploy',0,[x,y]))
            
        if button == (0,1,0):
          if self.selected_squad.motorized_state == "loaded":
            self.addOrder(self.selected_squad,Order('unload',0,[x,y]))
          else:
            if self.selected_squad.motorized_state == "unloaded":
              self.addOrder(self.selected_squad,Order('load',0,[x,y]))
              self.addOrder(self.selected_squad.bound_squad,Order('load',0,[x,y]))
            else:
              self.addOrder(self.selected_squad.bound_squad,Order('load',0,[x,y]))
              self.addOrder(self.selected_squad,Order('load',0,[x,y]))
             
          #~ if len(self.selected_squad.orders)>1:
             #~ self.addOrder(self.selected_squad,Order('unload',0,[x,y]))
          #~ else:
            
            #~ self.selected_squad.order_status = 'awaiting'
            #~ self.selected_squad.orders = []
            #~ self.selected_squad.path = []
            #~ self.selected_squad.subposition = 0
            
  def setPlayableCommander(self,name):
    for commander in self.commanders:
      if commander.name == name:
        self.playable_commander = commander
        return
    
    raise "No such commander"
    
  def addCommander(self,name,input_mode):
    self.commanders.append(Commander(name,input_mode))


class GameControls:
  def __init__(self):
    pass
class SynchronizationMechanics:
  def __init__(self,game):
    pass
  
  def makeStep(self,time):
    for squad in game.squads:
      if squad.state == 'moving':
        if len(squad.path)==0:
          squad.state = 'idle'
          continue
        if squad.subposition == 100:
          squad.pos = squad.path[0]
          if(len(squad.path>1)):
            squad.path = squad.path[1:]
          else:
            squad.path = []
            squad.state = 'idle'
            continue
        else:
          squad.subposition = squad.subposition + speed
  
